home *** CD-ROM | disk | FTP | other *** search
- /*
- HASWindows.c from Hsoi's App Shell © 1995-1997 John C. Daub. All rights reserved.
-
- This file deals with window related stuff: from the creation and destruction of windows,
- to window events (drags, content clicks, etc), to the little utility routines that
- help it all.
- */
-
- #pragma mark ••• #includes •••
-
- #ifndef _WASTE_
- #include "WASTE.h"
- #endif
- #include "HASGlobals.h"
- #ifndef __HSOIS_APP_SHELL__
- #include "HASMain.h"
- #endif
- #include "HASDialogs.h"
- #include "HASMenus.h"
- #include "HASUtilCursors.h"
- #include "HASMenuWindows.h"
- #include "HASFiles.h"
- #include "HASUtilFiles.h"
- #include "HASWindows.h"
- #include "HASLongControls.h"
- #include "HASUtilities.h"
- #include "HASUtilPStrings.h"
- #include "HASPrinting.h"
- #include "HASSoundSpeech.h"
-
- #include "WETabs.h"
-
- #ifndef _WASTEOBJECTS_
- #include "WASTE_Objects.h"
- #endif
-
- #if HAS_DEBUG
- #include "HASUtilTest.h"
- #endif
-
- #pragma mark -
- #pragma mark ••• Globals •••
-
- // local global variables
-
- //static WETranslateDragUPP sWEDragTranslator = nil;
- static WEScrollUPP sWEScroller = nil;
- static ControlActionUPP sScrollProc = nil;
- static long sScrollStep = 0; // how many pixels to scroll (used by ScrollProc)
-
-
- #pragma mark -
- #pragma mark ••• Window Utils •••
-
- // this calculates the rect for the grow icon (the little thing in the lower right
- // hand corner of the window)
-
- void HsoiCalcGrowIconRect( WindowRef window, Rect *iconRect )
- {
- Rect portRect = GetWindowPort(window)->portRect;
-
- iconRect->top = portRect.bottom - (kScrollBarWidth - 2);
- iconRect->left = portRect.right - (kScrollBarWidth - 2);
- iconRect->bottom = portRect.bottom;
- iconRect->right = portRect.right;
-
- return;
- }
-
- // this calcs the rect within the window where we draw the text...basically,
- // from the window's portRect, inset things a bit for scrollbars and then a
- // bit more so text doesn't get slammed up against the edges
-
- void HsoiCalcTextRect( WindowRef window, Rect *textRect )
- {
- Rect portRect = GetWindowPort(window)->portRect;
-
- textRect->top = 0;
- textRect->left = 0;
- textRect->bottom = portRect.bottom - (kScrollBarWidth - 1);
- textRect->right = portRect.right - (kScrollBarWidth - 1);
- InsetRect( textRect, kTextMargin, kTextMargin );
-
- return;
- }
-
- // calculate the rect that would enclose the scrollbars
-
- void HsoiCalcScrollBarRect( WindowRef window, VHSelect axis, Rect *barRect )
- {
- Rect portRect = GetWindowPort(window)->portRect;
-
- switch (axis)
- {
- case v:
- barRect->top = -1;
- barRect->left = portRect.right - (kScrollBarWidth - 1);
- barRect->bottom = portRect.bottom - (kScrollBarWidth - 2);
- barRect->right = portRect.right + 1;
- break;
-
- case h:
- barRect->top = portRect.bottom - (kScrollBarWidth - 1);
- barRect->left = -1;
- barRect->bottom = portRect.bottom + 1;
- barRect->right = portRect.right - (kScrollBarWidth - 2);
- break;
-
- default:
- break;
- }
-
- return;
- }
-
-
- /*
- HsoiCalcIdealWindowSize() attempts to determine the "ideal" size of a document window
- (the standard state of the window). This "ideal" window would be one in which the
- area of the window in which text is drawn (basically, the dest/view Rects of the
- WE instance) is the same as the area on which the text would be printed (taking
- into account the size of the printer's paper, the margin settings from the prefs).
- Then, pixels will be added for scroll bars etc... This is in an attempt to give
- a WYSIWYG look to the document windows...i always disliked how some text editors
- would give you some certain (or random) window size cause when you went to print,
- everything would be resized...no way really to ensure that what you see on the
- screen and what you print can be the same (such as where line breaks would occur
- and stuff).
-
- But like i said, this function only calculates the ideal size. The Rect that
- you pass as an argument doesn't contain coordinates of where the ideal window
- should be, but rather just contains measurements (for example, subtracting
- rect.left from rect.right would give you the ideal width of the window, again
- taking not only the text rect into account, but also the right/horizontal
- scroll bar and some buffer pixels). these measurements would give you the
- size of the window's portRect/contRgn.rgnBBox (see next comment block).
- If you want to get the window's strucRgn.rgnBBox, you'll have to manipulate
- this idealRect some (see HsoiZoomWindow for an instance where i do this)
-
- */
-
- /*
- This is also a good time to talk a little bit about the various regions of a window.
- There are many regions to a window (size region, drag region, close region, and zoom
- region will won't be discussing here, nor the update region, etc). What i want to
- talk about are the content region and the structure region.
-
- First, there is what's known as the window frame. the frame is comprised of the
- title bar (including the close and zoom boxes, if present), and the outline of
- the window itself. The drawing of the window frame is handled by the Window Manager
- (the WM also draws the size box, but only when YOU call the DrawGrowIcon function).
- Then there is the content region. This region you are responsible for drawing.
- The content region is just that: the contents of the window. This can include
- the size box, window controls (including any scroll bars you might have), and
- of course the window contents (like text or a PICT or whatever). When you put
- the window frame and the content region together, this makes up the structure
- region (the entire screen area occupied by a window, including the window outline,
- title bar, and content region).
-
- These various regions (content and structure) are parts of the WindowRecord (from
- which WindowPeek's are derrived. I dunno how this will all change with Copland.
- I'm sure the window parts will remain, but how the data structure of a WindowRef
- is constructed we'll just have to wait until the official Copland API is released
- to see). The strucRgn and contRgn members of the WindowRecord are expressed
- in global coordinates. It is important to know this because of the following:
- Part of the WindowRecord is a GrafPtr. This GrafPtr contains the window's
- portRect. the portRect (becomes important with calls like SizeWindow) of the
- window is equal to the content region of the window, however the portRect is
- expressed in local coordinates (while the contRgn is expressed in global).
-
- For more information on this stuff, see Inside Macintosh: Toolbox Essentials.
- Chapter 4 is all about the Window Manager, and specifically pages 4-12, 4-13
- cover this information about window regions.
- */
-
-
- void HsoiCalcIdealWindowSize( Rect *idealRect )
- {
- LongRect workLongRect;
- Rect margins;
-
- // first, let's get the rough area for the ideal size
-
- // we need to determine the margins based upon the preferences
- // we pass a "kludgy" number for the direction to force HsoiInchesToDots
- // to return a value * 72 dpi (see HsoiInchesToDots and IM:Imaging With QuickDraw
- // 5-32 (the note under ScreenRes()) for more information as to why i did this)
-
- margins.left = HsoiInchesToDots( gMyPrefs.printLeftMargin, kForce72dpi );
- margins.right = HsoiInchesToDots( gMyPrefs.printRightMargin, kForce72dpi );
- margins.top = HsoiInchesToDots( gMyPrefs.printTopMargin, kForce72dpi );
- margins.bottom = HsoiInchesToDots( gMyPrefs.printBottomMargin, kForce72dpi );
-
- // now get the dimensions of the actual printed rect
-
- HsoiGetPrintRect( &margins, &workLongRect, false );
- WELongRectToRect( &workLongRect, idealRect );
-
- // now idealRect contains a rough estimate of what the window's text rect
- // would be. but we need to expand this rect to essentially be the size
- // as the window's structure region.
-
- // first, add some pixels to the right and bottom for the scroll bars
- // (these measurements and numbers must be in sync with HsoiCalcScrollBarRect()'s
- // numbers so things don't get redrawn funky).
-
- // vertical/right scroll bar
-
- (*idealRect).right += kScrollBarWidth + 2;
-
- // horizontal/bottom scroll bar
-
- (*idealRect).bottom += kScrollBarWidth + 2;
-
- // inset (grow) the rect a bit (for happiness with HsoiCalcTextRect)
-
- InsetRect( idealRect, -kTextMargin, -kTextMargin );
-
- // and now idealRect contains our ideal size (measurements that can be used
- // to figure out the portRect/contRgn.rgnBBox) so just return!
-
- return;
- }
-
- // this takes a window and ensures that it's not extending off the edges of the
- // monitor(s). if it is, bump it back onto the screen
-
- void HsoiAdjustWindowIntoScreen( WindowRef window )
- {
- Rect deskRect;
- Rect portRect = GetWindowPort( window )->portRect;
- GrafPtr savePort;
-
- // back in the day (the day? when exactly was that?) :)
- // anyways, before we really had to deal with multiple monitor setups (i.e. before
- // the advent of Color QuickDraw), we could just get all the monitor real estate
- // from screenBits.bounds. but now with Color Quickdraw, all that's changed.
- // so, if the computer has color quickdraw, we get ALL the real estate that might
- // be there...but if they don't have color qd, we know they can't have a multi
- // monitor setup, so just get things from qd.screenBits.bounds
-
- if ( gHasColorQD )
- deskRect = (*GetGrayRgn())->rgnBBox;
- else
- deskRect = qd.screenBits.bounds;
-
- // set our port to the window to be adjusted (this is necessary so that
- // LocalToGlobal and GlobalToLocal know how to convert coordinates)
-
- GetPort( &savePort );
- SetPortWindowPort( window );
-
- // convert our window's portRect to global coordinates for comparison
- // against the deskRect
-
- LocalToGlobal( &topLeft(portRect) );
- LocalToGlobal( &botRight(portRect) );
-
- // bump the window's portRect within the screen area
-
- // first, the right side
-
-
- while ( portRect.right + 5 > deskRect.right )
- portRect.right--;
-
- // and now the bottom side
-
- while ( portRect.bottom + 5 > deskRect.bottom )
- portRect.bottom--;
-
- // (i probably ought to do the top and left also, and frankly to really make this
- // robust, i ought to ensure this window is totally on 1 monitor (not spread across
- // 2 or more monitors) and if not, move it totally within one monitor's screen.
- // if this troubles you, feel free to adjust this function! you might want to
- // look at HsoiZoomWindow for an idea of how to deal with multiple monitors)
-
- // convert the portRect back to local coordinates
-
- GlobalToLocal( &topLeft(portRect) );
- GlobalToLocal( &botRight(portRect) );
-
- // and assign this new portRect back to the window record. (hopefully this
- // won't break under Copland....we'll have to wait and see)
-
- GetWindowPort(window)->portRect = portRect;
-
- return;
- }
-
- #pragma mark -
- #pragma mark ••• Window/ScrollBar Utils •••
-
- // does just what you think...draws the grow icon in the lower right corner
- // of the window
-
- void HsoiMyDrawGrowIcon( WindowRef window, Boolean validate )
- {
- GrafPtr savePort;
- RgnHandle saveClip;
- Rect r;
-
- // save port and set the port to wind
-
- GetPort( &savePort );
- SetPortWindowPort( window );
-
- // save the clip region
- saveClip = NewRgn();
- GetClip( saveClip );
-
- // calculate grow icon rect
- HsoiCalcGrowIconRect( window, &r );
-
- // set clip region to grow icon rect
- ClipRect( &r );
-
- // call _DrawGrowIcon
-
- DrawGrowIcon( window );
-
- // if validate is true, remove the grow icon rect from the update region
- if ( validate )
- ValidRect( &r );
-
- // restore old clip region
-
- SetClip( saveClip );
- DisposeRgn( saveClip );
-
- // restore old port
- SetPort( savePort );
-
- return;
- }
-
- // if the value of the scroll bar has changed (cause perhaps someone clicked in it),
- // call this to adjust things
-
- void HsoiScrollBarChanged( WindowRef window )
- {
- WEReference we;
- LongRect viewRect, destRect;
-
- // get the window's WE instance
-
- we = HsoiGetWindowWE( window );
-
- // get the view and dest rects
-
- WEGetViewRect( &viewRect, we );
- WEGetDestRect( &destRect, we );
-
- // and scroll the text
-
- WEScroll( viewRect.left - destRect.left - HsoiLCGetValue( ((*HsoiGetWindowDocument(window))->scrollBars).h),
- viewRect.top - destRect.top - HsoiLCGetValue( ((*HsoiGetWindowDocument(window))->scrollBars).v), we );
-
- return;
- }
-
- // this adjusts and updates our scroll bars to keep them in sync with the text
-
- void HsoiAdjustBars( WindowRef window )
- {
- DocumentHandle hDocument;
- WEReference we;
- GrafPtr savePort;
- LongRect viewRect, destRect;
- long value;
- long max;
- ControlRef bar;
-
- GetPort( &savePort );
- SetPortWindowPort( window );
-
- // get the view and destination rectangle
-
- hDocument = HsoiGetWindowDocument(window);
- we = (*hDocument)->we;
-
- WEGetViewRect( &viewRect, we );
- WEGetDestRect( &destRect, we );
-
- // do the vertical axis
-
- // get scroll bar handle
-
- bar = ((*hDocument)->scrollBars).v;
-
- // calculate new scroll bar settings
-
- // NOTE: (destRect.bottom - destRect.top) always equals the total text height because
- // WASTE automatically updates destRect.bottom whenever line breaks are recalculated
-
- value = viewRect.top - destRect.top;
-
- // bug fix from Dan Crevier (thanx Dan). I was adding viewRect + viewRect...dumb...
-
- max = value + (destRect.bottom - viewRect.bottom);
-
- // make sure max is always non-negative
-
- if ( max <= 0 )
- max = 0;
-
- // reset the scroll bar
-
- HsoiLCSetMax( bar, max );
- HsoiLCSetValue( bar, value );
-
- // if value exceeds max then the bottom of the destRect is above
- // the bottom of the view rectangle: we need to scroll the text downward
-
- if ( value > max )
- HsoiScrollBarChanged( window );
-
- // now do the horizontal axis
-
- // get scroll bar handle
-
- bar = ((*hDocument)->scrollBars).h;
-
- // calculate new scroll bar settings
-
- // NOTE: (destRect.bottom - destRect.top) always equals the total text height because
- // WASTE automatically updates destRect.bottom whenever line breaks are recalculated
-
- value = viewRect.left - destRect.left;
-
- // again, bug fix from Dan Crevier (thanx again). I was adding viewRect.right and viewRect.right
- // when it should be:
-
-
- max = value + (destRect.right - viewRect.right);
-
- // make sure max is always non-negative
-
- if ( max <= 0 )
- max = 0;
-
- // reset the scroll bar
-
- HsoiLCSetMax( bar, max );
- HsoiLCSetValue( bar, value );
-
- // if value exceeds max then the bottom of the destRect is above
- // the bottom of the view rectangle: we need to scroll the text downward
-
- if ( value > max )
- HsoiScrollBarChanged( window );
-
- SetPort( savePort );
-
- return;
- }
-
-
- // Fix it's scroll bars and WE view rect when the window is created
- // or after it is resized or zoomed, or when the page is adjusted
-
- void HsoiViewChanged( WindowRef window )
- {
- DocumentHandle hDocument;
- GrafPtr savePort;
- ControlRef bar;
- Rect r;
- LongRect viewRect;
-
- GetPort( &savePort );
- SetPortWindowPort( window );
-
- hDocument = HsoiGetWindowDocument(window);
-
- // recalculate the correct rectangles for the text area and the scroll bars,
- // based on the window's port rect
-
- HsoiCalcTextRect( window, &r );
- WERectToLongRect( &r, &viewRect );
-
- // resize the text area
-
- WESetViewRect( &viewRect, (*hDocument)->we );
-
- // more and resize the control bars
-
- // first, the vertical bars
-
- bar = ((*hDocument)->scrollBars).v;
- HsoiCalcScrollBarRect( window, v, &r );
- MoveControl( bar, r.left, r.top );
- SizeControl( bar, r.right - r.left, r.bottom - r.top );
- ValidRect( &r );
-
- // now the horizontal bars
-
- bar = ((*hDocument)->scrollBars).h;
- HsoiCalcScrollBarRect(window, h, &r);
- MoveControl( bar, r.left, r.top );
- SizeControl( bar, r.right - r.left, r.bottom - r.top );
- ValidRect( &r );
-
- // reset the thumb positions and the max values of the control bars
- HsoiAdjustBars( window );
-
- // redraw the control bars
-
-
- ShowControl( ((*hDocument)->scrollBars).v );
- ShowControl( ((*hDocument)->scrollBars).h );
-
- SetPort( savePort );
-
- return;
-
- }
-
- #pragma mark -
- #pragma mark ••• Window Event Handlers •••
-
- // this resizes our window, like when the window is to be grown via the grow box
- // see HsoiDoGrow() for more info...
-
- void HsoiResize( Point newSize, WindowRef window )
- {
- DocumentHandle hDocument;
- GrafPtr savePort;
- Rect r;
- RgnHandle tempRgn, dirtyRgn;
-
- GetPort( &savePort );
- SetPortWindowPort( window );
-
- hDocument = HsoiGetWindowDocument(window);
-
- // create temporarty regions for calculations
- tempRgn = NewRgn();
- dirtyRgn = NewRgn();
-
- // save old text region
-
- HsoiCalcTextRect( window, &r );
- RectRgn( tempRgn, &r );
-
- // erase the old grow icon rect
- HsoiCalcGrowIconRect( window, &r );
- EraseRect( &r );
-
- // hide the scroll bars
-
- HideControl( ((*hDocument)->scrollBars).v );
- HideControl( ((*hDocument)->scrollBars).h );
-
- // perform the actual resizing of the window, redraw scroll bars and grow icon
- SizeWindow( window, newSize.h, newSize.v, false );
- HsoiViewChanged( window );
- HsoiMyDrawGrowIcon( window, true );
-
- // calculate the dirty region (to be updated)
- HsoiCalcTextRect( window, &r );
- RectRgn( dirtyRgn, &r );
- XorRgn( dirtyRgn, tempRgn, dirtyRgn );
- InsetRect( &r, -kTextMargin, -kTextMargin );
- RectRgn( tempRgn, &r );
- SectRgn( dirtyRgn, tempRgn, dirtyRgn );
-
- // mark the dirty region as invalid
-
- InvalRgn( dirtyRgn );
-
- // throw away temporary regions
-
- DisposeRgn( tempRgn );
- DisposeRgn( dirtyRgn );
-
- SetPort( savePort );
-
- return;
- }
-
- // this is the function called when we're growing the window.
-
- void HsoiDoGrow( Point hitPt, WindowRef window )
- {
- Rect sizeRect;
- long newSize;
- Point tempPoint;
-
- SetRect( &sizeRect, kMinWindowWidth, kMinWindowHeight, MAXSHORT, MAXSHORT );
- newSize = GrowWindow( window, hitPt, &sizeRect );
-
-
- // In the WASTE Demo App source, Marco typecasted newSize to a type Point. Can't
- // do that in C (but you can, obviously, in Pascal).
- // But there is a trick! The Point structure is 32-bits, with the v value
- // in the hi word and the h value in the low word. So, we can just separate
- // them out. Neat huh?
-
-
-
- if ( newSize != 0 )
- {
- tempPoint.v = HiWrd( newSize );
- tempPoint.h = LoWrd( newSize );
-
- HsoiResize( tempPoint, window );
- }
- return;
- }
-
- /*
- HsoiZoomWindow() is a replacement function for the toolbox call, ZoomWindow().
- ZoomWindow is ok, but isn't always the smartest way to do things, especially
- in a multiple monitor setup (for example, the user might have moved the window
- to a different screen since it was last zoomed).
-
- This "replacement" procedure (for ZoomWindow) is pretty much taken stock
- from Inside Macintosh:Imaging with QuickDraw:Graphics Devices:5-10
- except that I've translated it all into C and am working towards as much
- Copland compatability as possible.
-
- Here's what Apple has to say about it:
-
- This is a procedure "which an application might call when the user clicks the
- zoom box. Because the user might have moved the window to a different screen
- since it was last zoomed, the procedure first determines which screen contains
- the largest area of the window and then calcuates the ideal window size for
- that screen before zooming the window.
-
- The screen calculations in the [HsoiZoomWindow] procedure compare GDevice
- records stored in the device list (if Color QuickDraw is not available,
- [HsoiZoomWindow] assumes that it's running on a computer with a single screen."
-
- After the sample code, Apple adds:
-
- "If the user is zooming the window to the standard state, [HsoiZoomWindow] calculates
- a new standard size and location based on the application's own considerations,
- the current location of the window, and the available screens. The [HsoiZoomWindow]
- procedure always places the standard state on the screen where the window is currently
- displayed or, if the window spans screens, on the scren containing the largest
- area of the window.
-
- [This sample code listing] uses the QuickDraw routines GetDeviceList, TestDeviceAttribute,
- GetNextDevice, SectRect, and GetMainDevice to examine characteristics of
- the available screens as stored in GDevice records. Most of the code in
- [this sample code listing] is devoted to determining which screen should display
- the window in the standard state.
-
- **IMPORTANT**
- Never use the bounds field of a PixMap record to determine the size of the
- screen; instead, use the value of the gdRect field of the GDevice record for
- the screen (as shown here)
-
- After calculating the standard state, if necessary, [HsoiZoomWindow] calls
- the ZoomWindow procedure to redraw the window frame in the new size and
- location."
-
- In a future release of Hsoi's App Shell, I hope to modify this a little bit.
- I would like to have it such that the window's "standard state" (as opposed
- to the "user state") is the size (the text editing part...not counting things
- like the title bar and scroll bars) of the window is the size of the paper
- (to keep with as much WYSIWYG as possible). That is, if the user will
- be printing on an 8.5" x 11" piece of paper, the window is 8.5 x 11. If
- an envelope, the window is the size of an envelope. Of course, all things
- will take into account the screen size (so that windows don't extend offscreen
- somewhere). See IM:Imaging with QuickDraw: 5-9 for more details.
-
- update: i've done the above...based upon the size of the paper to be
- printed on (and other things, like margins), the standard state will now
- be "WYSIWYG" and happy for the user.
-
- */
-
- void HsoiZoomWindow( WindowRef thisWindow, short zoomInOrOut )
- {
- GDHandle gdNthDevice, gdZoomOnThisDevice;
- GrafPtr savePort;
- Rect windRect, zoomRect, theSect, portRect;
- long sectArea, greatestArea;
- short wTitleHeight;
- Boolean sectFlag;
- RgnHandle windStructureRgn = NewRgn();
-
- // first, get the old port and set the new port to our window
-
- GetPort( &savePort );
- SetPortWindowPort( thisWindow );
-
- // erase the window to avoid flicker
-
- EraseRect( &GetWindowPort( thisWindow )->portRect );
-
- // if zooming to standard state
-
- if ( zoomInOrOut == inZoomOut )
- {
- // assume a single screen (you need color quickdraw in order to support
- // multiple monitors...no color qd, no way they'd have more than 1 monitor)
- // and...
-
- if ( !gHasColorQD )
- {
- // ... set standard state to the full screen
-
- // zoomRect = qd.screenBits.bounds;
- // InsetRect( &zoomRect, 4, 4 );
-
- // we're not gonna do the above...we want to have the window zoomed
- // to an "ideal" size. see below for more information
-
- HsoiCalcIdealWindowSize( &portRect );
-
- // now set our zoomRect to the proper global coordinates.
-
- SetRect( &zoomRect, qd.screenBits.bounds.left + 3,
- qd.screenBits.bounds.top + kTitleBarHeight + LMGetMBarHeight() + 3,
- portRect.right - portRect.left + 3,
- portRect.bottom - portRect.top + 3 );
-
- if ( zoomRect.right > qd.screenBits.bounds.right )
- zoomRect.right = qd.screenBits.bounds.right - 2;
-
- if ( zoomRect.bottom > qd.screenBits.bounds.bottom )
- zoomRect.bottom = qd.screenBits.bounds.bottom - 7;
-
- SetWindowStandardState( thisWindow, &zoomRect );
- }
- else // locate the window on available screens
- {
- windRect = GetWindowPort( thisWindow )->portRect;
-
- // convert to global coordinates
-
- LocalToGlobal( &topLeft(windRect) );
- LocalToGlobal( &botRight(windRect) );
-
- // calculate the height of the window's title bar
-
- GetWindowStructureRgn( thisWindow, windStructureRgn );
- wTitleHeight = windRect.top - 1 - (*windStructureRgn)->rgnBBox.top;
- windRect.top -= wTitleHeight;
-
- // get the first screen
-
- gdNthDevice = GetDeviceList();
- greatestArea = 0; // initialize area to zero
-
- // check window against all gdRects in gDevice list and remember
- // which gdRect contains largest area of window
-
- while ( gdNthDevice != nil )
- {
- if ( TestDeviceAttribute( gdNthDevice, screenDevice ) &&
- TestDeviceAttribute( gdNthDevice, screenActive ) )
- {
- // the SectRect function calculates the intersection of the window
- // rectangle and this GDevice's boundary rectangle and returns
- // true if the rectangles intersect, false if they don't
-
- sectFlag = SectRect( &windRect, &(*gdNthDevice)->gdRect, &theSect );
-
- // determine which screen holds greatest window area
- // first, calculate area of rectangle on current screen
-
- sectArea = ((long)theSect.right - (long)theSect.left) *
- ((long)theSect.bottom - (long)theSect.top);
-
- if ( sectArea > greatestArea )
- {
- // set greatest area so far
-
- greatestArea = sectArea;
-
- // set zoom device
-
- gdZoomOnThisDevice = gdNthDevice;
- }
-
- // get next GDevice record
-
- gdNthDevice = GetNextDevice( gdNthDevice );
- }
-
- } // end: while ( gdNthDevice != nil )
-
-
- // if gdZoomOnThisDevice is on main device, allow for menu bar height
-
- if ( gdZoomOnThisDevice == GetMainDevice() )
- wTitleHeight += LMGetMBarHeight();
-
- // create the zoom rectangle
-
- // now, the commented stuff that follows is what was in the original
- // code example from Inside Macintosh. it sets the zoom rectangle to the
- // full screen, minus window title height (and menu bar height if necessary),
- // inset by 3 pixels.
-
- // incidentally, this is what ZoomWindow() normally does. So why have this
- // routine to "replace" ZoomWindow()? cause remember, ZoomWindow() doesn't
- // know how to deal with mulitple monitor setups (hopefully, Apple will
- // change this for Copland)
-
- // SetRect( &zoomRect, (*gdZoomOnThisDevice)->gdRect.left + 3,
- // (*gdZoomOnThisDevice)->gdRect.top + wTitleHeight + 3,
- // (*gdZoomOnThisDevice)->gdRect.right - 3,
- // (*gdZoomOnThisDevice)->gdRect.bottom - 3 );
-
- // but since we're cool and have our own ideal/standard window state, let's
- // figure out the ideal size of the window (remember, this will
- // return measurements (not coordinates) that would give you
- // the portRect/contRgn.rgnBBox of the window. we'll need to adjust to make
- // this rect the size of the structure region
-
- HsoiCalcIdealWindowSize( &portRect );
-
- // now set our zoomRect to the proper global coordinates.
-
- SetRect( &zoomRect, (*gdZoomOnThisDevice)->gdRect.left + 3,
- (*gdZoomOnThisDevice)->gdRect.top + wTitleHeight + 3,
- portRect.right - portRect.left + 3,
- portRect.bottom - portRect.top + 3 );
-
- // adjust in case the window is extending off the edges of the screen
- // (we don't have to do top and left cause if you notice in SetRect,
- // the top and left of the window are determined by the device the
- // window is currently on).
-
- if ( zoomRect.right > (*gdZoomOnThisDevice)->gdRect.right )
- zoomRect.right = (*gdZoomOnThisDevice)->gdRect.right - 2;
-
- if ( zoomRect.bottom > (*gdZoomOnThisDevice)->gdRect.bottom )
- zoomRect.bottom = (*gdZoomOnThisDevice)->gdRect.bottom - 7;
-
- // set up the WStateData record for this window.
-
- SetWindowStandardState( thisWindow, &zoomRect );
-
- }
-
- } // end if ( zoomInOrOut == inZoomOut )
- // if zoomInOrOut == inZoomIn, just let ZoomWindow zoom to user state
-
-
- // zoom the window frame
-
- ZoomWindow( thisWindow, zoomInOrOut, (thisWindow == FrontWindow()) );
-
- // in the Inside Macintosh sample that HsoiZoomWindow() was based upon, here
- // they called MyResizeWindow(thisWindow), which is an application-defined
- // window sizing routine. However, due to how HsoiDoZoom() is set up, we
- // don't need to do that stuff here (it's done in HsoiDoZoom() instead)
-
- // dispose of the region
-
- if ( windStructureRgn != nil )
- DisposeRgn( windStructureRgn );
-
- // restore the port
-
- SetPort( savePort );
-
- return;
- }
-
- // this is the function called when a zoom is called for
-
- void HsoiDoZoom( short partCode, WindowRef window )
- {
- DocumentHandle hDocument;
- GrafPtr savePort;
- Rect r;
-
- // set up the ports
-
- GetPort( &savePort );
- SetPortWindowPort( window );
-
- // get a handle to the DocumentRecord
-
- hDocument = HsoiGetWindowDocument(window);
-
- // erase our port rect (for better redrawing)
-
- r = GetWindowPort( window )->portRect;
- EraseRect( &r );
-
- // hide the controls (else risk a messy looking zoom)
-
- HideControl( ((*hDocument)->scrollBars).v );
- HideControl( ((*hDocument)->scrollBars).h );
-
- // do the actual zooming
-
- HsoiZoomWindow( window, partCode );
-
- // and get things updated...
-
- // note the view has been changed and readjust
-
- HsoiViewChanged( window );
-
- // find the new text rect...
-
- HsoiCalcTextRect( window, &r );
-
- // ...and invalidate it for updating by the Window Manager
-
- InvalRect( &r );
-
- // restore the old port, and off we go!
-
- SetPort( savePort );
-
- return;
- }
-
- // this is a callback routine called by the Toolbox Control Manager
- // move the scroll bar thumb and scroll the text accordingly
-
-
- pascal void hsoiScrollProc( ControlRef bar, ControlPartCode partCode )
- {
- long value, step;
-
- if ( partCode == kControlNoPart ) // if partCode == 0 (kControlNoPart),
- // then mouse is outside of the control..return...
- return;
-
- // get the value of the scrollbar
-
- value = HsoiLCGetValue( bar );
-
- // find our step
-
- step = sScrollStep;
-
- // if the value of the scrollbar is less than max and greater and zero, and
- // the value is greater than the min (zero) and less than the step...
-
- if ( (( value < HsoiLCGetMax( bar )) && ( step > 0 )) || (( value > 0 ) && ( step < 0 ) ) )
- {
- // ...set the value of the bar at += step...
-
- HsoiLCSetValue( bar, value + step );
-
- // ...and update the scroll bars (scroll the text)
-
- HsoiScrollBarChanged( FrontWindow() );
- }
-
- return;
- }
-
- // when the mouse is clicked in a scrollbar, we call this.
-
- void HsoiDoScrollBar( Point hitPt, EventModifiers modifiers, WindowRef window )
- {
- DocumentHandle hDocument;
- ControlRef bar;
- LongRect viewRect;
- ControlPartCode partCode;
- short step;
-
- hDocument = HsoiGetWindowDocument(window);
- WEGetViewRect(&viewRect, (*hDocument)->we);
-
- // find out which scrollbar was hit (if any) and in which part
- partCode = FindControl( hitPt, window, &bar );
-
- if ( bar != nil )
- {
- // dispatch on partCode
-
- if ( partCode == kControlIndicatorPart )
- {
- // click in thumb: call TrackControl with no actionProc and adjust text
-
- partCode = TrackControl( bar, hitPt, nil );
-
- // keep the MacOS short scrollbar values in sync with our long values
-
- HsoiLCSynch( bar );
-
- // and actually scroll the text (the WEScroll call)
-
- HsoiScrollBarChanged( window );
-
- } // end if partCode == inThumb
-
- else
-
- {
- // the vertical scroll bar
-
- if ( bar == ((*hDocument)->scrollBars).v )
- {
-
- // dispatch our partCode
-
- switch( partCode )
- {
- // when they click-hold in the up/down arrows, scroll by
- // the value of kScrollDelta, but if the option key is held
- // down while they scroll, only scroll by one pixel (allows
- // for more "delicate" scrolling)
-
- case kControlUpButtonPart:
- if ( (modifiers & optionKey ) == 0 )
- step = -kScrollDelta;
- else
- step = -1;
-
- break;
-
- case kControlDownButtonPart:
- if ( (modifiers & optionKey ) == 0 )
- step = +kScrollDelta;
- else
- step = 1;
-
- break;
-
- // if they clicked in the page up/down parts, jump a "page"
-
- case kControlPageUpPart:
- step = -( viewRect.bottom - viewRect.top) + kScrollDelta;
-
- break;
-
- case kControlPageDownPart:
- step = ( viewRect.bottom - viewRect.top ) - kScrollDelta;
-
- break;
-
- default:
- step = 0;
- }
- }
-
- // else if the horizontal scroll bar...
-
- else if ( bar == ((*hDocument)->scrollBars).h )
- {
- // dispatch our partCode
-
- switch( partCode )
- {
- case kControlUpButtonPart:
- if ( (modifiers & optionKey ) == 0 )
- step = -kScrollDelta;
- else
- step = -1;
-
- break;
-
- case kControlDownButtonPart:
- if ( (modifiers & optionKey ) == 0 )
- step = +kScrollDelta;
- else
- step = 1;
-
- break;
-
- case kControlPageUpPart:
- step = -( viewRect.right - viewRect.left) + kScrollDelta;
-
- break;
-
- case kControlPageDownPart:
- step = ( viewRect.right - viewRect.left ) - kScrollDelta;
-
- break;
-
- default:
- step = 0;
- }
- }
-
- // save step in a static variable for our ScrollProc callback
-
- sScrollStep = step;
-
- // track the mouse
-
- if ( sScrollProc == nil )
- sScrollProc = NewControlActionProc( hsoiScrollProc );
-
- partCode = TrackControl( bar, hitPt, sScrollProc );
-
- if ( sScrollProc != nil )
- {
- DisposeRoutineDescriptor( sScrollProc );
- sScrollProc = nil;
- }
- }
-
- }
-
- return;
- }
-
- // this is a callback routine called whenever the text is scrolled automatically.
- // Since auto-scrolling is enabled, WEScroll may be invoked internally by WASTE
- // in many different circumstances, and we want to be notified when this happens
- // so we can adjust the scroll bars
-
- pascal void hsoiTextScrolled( WEReference we )
- {
- WindowRef window = nil;
-
- // retrieve the window pointer stored in the WE instance as a "reference constant"
-
- if (WEGetInfo(weRefCon, &window, we) != noErr )
- return;
-
- // make sure the scroll bars are in synch with the destination rectangle
-
- HsoiAdjustBars( window );
-
- return;
- }
-
- // handle the use of the pageup, pagedown, home and end keys on the extended keyboard
-
- void HsoiDoScrollKey( SignedByte keyCode, WindowRef window )
- {
- DocumentHandle hDocument;
- ControlRef bar;
- long v;
- LongRect viewRect;
-
- hDocument = HsoiGetWindowDocument(window);
- bar = ((*hDocument)->scrollBars).v;
-
- // get current scroll bar value
-
- v = HsoiLCGetValue( bar );
-
- // get text view rect
-
- WEGetViewRect( &viewRect, (*hDocument)->we );
-
- switch ( keyCode )
- {
-
- case keyPgUp:
- v = v - ( viewRect.bottom - viewRect.top ) + kScrollDelta;
-
- break;
-
- case keyPgDn:
- v = v + ( viewRect.bottom - viewRect.top ) - kScrollDelta;
-
- break;
-
- case keyHome:
- v = 0;
-
- break;
-
- case keyEnd:
- v = MAXLONG;
- break;
-
- default:
-
- break;
- } // end switch keyCode
-
-
- // set the new scroll bar value and scroll the text pane accordingly
-
- HsoiLCSetValue( bar, v );
- HsoiScrollBarChanged( window );
-
-
- return;
- }
-
- // this is what's called when the user chooses Close from the File menu.
-
- OSErr HsoiDoClose( ClosingOption closing, SavingOption saving, WindowRef window )
- {
- Str255 title, quitOrClose;
- short alertResult;
- OSErr err = noErr;
-
- // it should never hit this (cause if window == nil, the close menu item should be
- // disabled anyways, therefore no way to call this function. but let's be complete
-
- if ( window == nil )
- return err;
-
- // first, check the window kind...if a DA or a dialog, close it properly
-
- if ( HsoiIsDAWindow( window ) )
- {
- CloseDeskAcc( GetWindowKind(window) );
- return err;
- }
-
-
- if ( HsoiIsDialogWindow( window ) )
- {
- HideWindow( window );
- return err;
- }
-
- // we'll just hide our clipboard window
-
- if ( HsoiIsClipboardWindow( window ) )
- {
- // if there was a sound in the clipboard window that was playing, we should
- // stop it from playing. however, there isn't an easy way via Michael
- // Kamprath's WASTE Object Handlers to determine what window (WASTE instance)
- // the currently playing sound belongs to. maybe I'll write one and
- // submit it to Michael.
-
- // so, to facilitate things, we could do 1 of 2 things. instead of hiding
- // the clipboard window, destroy it. i don't want to do that. so,
- // if there is a sound playing, any sound, we'll just stop it.
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
-
- HideWindow( window );
-
- return err;
- }
-
- #if HAS_DEBUG
-
- if ( HsoiIsTestWindow( window ) )
- {
- HsoiDestroyTestWindow();
- return err;
- }
- #endif
-
- // is the window dirty?
-
- if ( WEGetModCount( HsoiGetWindowWE(window) ) > 0 )
- {
- // do we have to ask the user whether to save changes??
-
- if ( saving == savingAsk )
- {
- // since we're asking, stop any currently playing sounds before the
- // dialog is brought up
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
-
- // get the title of the window/document
-
- GetWTitle( window, title );
-
- // just in case there is no title, stick in a dummy title
-
- if ( title == NIL_STRING )
- HsoipStringCopy( "\puntitled", title );
-
- // get the string "closing" or "quitting" so that when this
- // alert is brought up, the text will br appropriate to the
- // context (try it! it's a nice subtle touch...launch the app,
- // type something in the window, don't save, close the window,
- // it'l say "before closing", cancel, then quit, it'll say
- // "before quitting" and then just don't save and quit. cute eh?)
-
- GetIndString( quitOrClose, rQuitCloseStrings, 1 + (closing ? 1 : 0 ) );
-
- // put the strings in the alert
-
- ParamText( title, quitOrClose, NIL_STRING, NIL_STRING );
-
- // put up the Save Changes? alert box?
-
- InitCursor();
-
- alertResult = Alert( rSaveChangesAlert, HsoiGetMyStandardDialogFilter() );
-
- // exit if the user canceled the alert box
-
- if ( alertResult == kButtonCancel )
- {
- err = userCanceledErr;
- return err;
- }
-
- if ( alertResult == kButtonSave )
- saving = savingYes;
- else
- saving = savingNo;
- }
-
- if ( saving == savingYes )
- {
- // no need to check for and possibly stop a sound here...HsoiDoSave takes
- // care of that for us.
-
- // save it
-
- err = HsoiDoSave( window );
- if ( err != noErr )
- {
- return err;
- }
- }
- } // end dirty window check
-
- // destroy the window
-
- HsoiDestroyWindow( window );
-
- return err;
- }
-
-
-
- //JCD - This "morally correct" code for window dragging is per an article in MacTech
- //JCD - Magazine (July 1994, Vol 10, No. 7) by Eric Shapiro (of Rock Ridge Enterprises)
- //JCD - called "Multiple Monitors vs. Your Application".
- //JCD -
- //JCD - Eric addresses numerous things to allow your app to deal nicely with multiple
- //JCD - monitor setups, one of them is Dragging.
- //JCD -
- //JCD - According to Eric, many apps don't let you drag windows to second monitors, and
- //JCD - though holding down the cmd/option keys often overrides this problem, it should
- //JCD - still be updated. And the only reason that qd.screenBits.bounds works to allow
- //JCD - you to drag to second monitors is because of a kludge Apple put in the Window Manager
- //JCD -
- //JCD - So what follows is Eric's "morally correct" code for dragging windows. thanx eric!
-
- void HsoiDoDrag( Point thePoint, WindowRef window )
- {
- Rect limitR;
-
- if ( gHasColorQD )
- limitR = ( *GetGrayRgn())->rgnBBox;
- else
- limitR = qd.screenBits.bounds;
-
- DragWindow( window, thePoint, &limitR );
-
- return;
- }
-
-
- // Handle clicks inside a window
-
- Boolean HsoiDoContentClick( Point hitPt, const EventRecord *event, WindowRef window )
- {
- WEReference we = HsoiGetWindowWE(window);
- long selStart, selEnd;
- RgnHandle selRgn;
- Boolean inBackground, handleClick;
- Rect textRect;
- GrafPtr savePort;
- Boolean result = false; // false means click should not activate this window
-
-
- // is this window in the background?
-
- if ( IsWindowHilited(window) )
- inBackground = false;
- else
- inBackground = true;
-
- #if HAS_DEBUG
- // if it's the test window, handling is pretty easy since it can't take
- // drags or really any other sort of click
-
- if ( HsoiIsTestWindow(window) )
- return inBackground;
- #endif
-
- // we don't want to let the below code handle anything but windows with
- // WASTE instances attached to them (else we risk crashing!). so, before
- // we go further down, let's check for a few things
-
- if ( HsoiIsDialogWindow(window) || HsoiIsDAWindow( window ) )
- return inBackground;
-
- // one last test just in case...cause if we have a nil WE instance, we will
- // crash.
-
- if ( we == nil )
- {
- // if window is valid (i.e. !nil), we can return based upon the
- // inBackground setting to still allow a "valid" return
-
- if ( window != nil )
- return inBackground;
- else
- {
- // we got a BIG problem that needs to be tracked down....window should
- // never be nil due to where this function is called from (in HsoiDoEvent
- // from the mouseDown inContent...that event shouldn't even happen
- // if window == nil
-
- SysBeep( 5 ); // lame error handling
- return false; // return false so hopefully nothing will happen upon return
- }
- }
-
- // set the port to our window's port
-
- GetPort( &savePort );
- SetPortWindowPort( window );
-
- // convert the point to local coordinates
-
- GlobalToLocal( &hitPt );
-
- // a click in an inactive window should normally activate it,
- // but the availability of the Drag Manager introduces an exception to this rule:
- // a click in the background selection may start a drag gesture,
- // without activating the window
-
- if ( inBackground )
- {
- if ( gHasDragAndDrop )
- {
- // see if the click was in a selection in a background window and
- // based on that, determine if we should handle the click or not
-
- WEGetSelection( &selStart, &selEnd, we );
- selRgn = WEGetHiliteRgn( selStart, selEnd, we );
- handleClick = PtInRgn( hitPt, selRgn ) && WaitMouseMoved( event->where );
- DisposeRgn( selRgn );
- }
- else
- handleClick = false; // no DragManager: never click-through
- }
- else
- handleClick = true; // window is frontmost: always handle click
-
- if ( handleClick )
- {
- // get view Rectangle in short coordinates
-
- HsoiCalcTextRect( window, &textRect );
-
- // if the click is in our text rect, let WEClick handle it (nice that
- // due to the fact WASTE has Internet Config support built into it,
- // this one little call can be Internet Config happy!)
-
- if ( PtInRect( hitPt, &textRect ) )
- {
- if ( !gHiliting )
- WEClick( hitPt, event->modifiers, event->when, we );
- }
- else
- // not in the text rect, scroll the bars
-
- HsoiDoScrollBar( hitPt, event->modifiers, window );
- }
- else
- result = inBackground;
-
- // restore the port
-
- SetPort( savePort );
-
-
- return result;
- }
-
- #pragma mark -
- #pragma mark ••• Creation/Destruction •••
-
-
- /* whenever an error kicks in ( err != noErr ), we should destroy partially allocated
- data structures to avoid memory leaks...make sure to add that stuff in! When you
- do, stick it just before the return statement. It's a good thing to call
- DisposePtr() after NewPtr() (or DisposeHandle() after NewHandle()) to make sure
- the heap don't get too fragmented up...see p. 33 of Knaster's How to Write for
- more info... */
-
- OSErr HsoiCreateWindow( const FSSpec *pFileSpec )
- {
- DocumentHandle hDocument;
- WindowRef window;
- WEReference we;
- ControlRef bar;
- FInfo fileInfo;
- Rect textRect;
- LongRect longTextRect;
- OSErr err = noErr;
- AliasHandle alHandle;
- Rect windowRect;
- Str255 titleString;
- Str255 numString;
- register short counter;
-
- // first of all, make sure we don't have the max number of windows already
- // open. if so, return.
- // also, don't update the gNumWindows counter here, just in case elsewhere in
- // creating the window we have to bail out...wait until the end
-
- if ( gNumWindows >= kMaxNumberOfOpenWindows )
- return -1; // "arbitrary" error code
-
- // make sure the numString is nil'ed out...
-
- for ( counter = 0; counter < 256; counter++ )
- {
- numString[ counter ] = nil;
- titleString[ counter ] = nil;
- }
-
- // allocate a relocateable block to hold a document record
-
- hDocument = (DocumentHandle)NewHandleClear( sizeof( DocumentRecord ) );
- err = MemError();
- if ( err != noErr )
- {
- if ( err == memFullErr )
- {
- // if we ran out of memory, see if we might be able to free some up.
- // an already open window (with a WASTE instance) might have a sound
- // playing in it. if so, we'll stop the current sound, free up the
- // memory the sound was occupying, and try again hoping this might help.
-
- if (SoundIsPlaying())
- {
- // first, stop the sound, if any
-
- StopCurrentSound();
-
- // now try to allocate the doc rec block
-
- hDocument = (DocumentHandle)NewHandleClear( sizeof( DocumentRecord ) );
-
- err = MemError();
- }
- }
-
- if ( err != noErr )
- HsoiDoError( rWindowErrorStrings, strCrWindNewPtrClear, err, kErrNote );
- return err;
- }
-
- // create the window from a 'WIND' template: the window is initially invisible
- // if ColorQuickDraw is available, create a color window
-
- if ( gHasColorQD )
- window = GetNewCWindow( rMainWindow, nil, MOVE_TO_FRONT );
- else
- window = GetNewWindow( rMainWindow, nil, MOVE_TO_FRONT );
-
- // make sure we got a window
-
- if ( window == nil )
- {
- // just like with the above check for playing sounds, try that same thing here
-
- if ( SoundIsPlaying() )
- {
- StopCurrentSound();
-
- if ( gHasColorQD )
- window = GetNewCWindow( rMainWindow, nil, MOVE_TO_FRONT );
- else
- window = GetNewWindow( rMainWindow, nil, MOVE_TO_FRONT );
- }
-
- if ( window == nil )
- {
- // failed (again), probably nothing we can do...
-
- HsoiForgetHandle( (Handle *)&hDocument );
- HsoiDoError( rWindowErrorStrings, strCrWindGetNewWind, memFullErr, kErrNote );
- return memFullErr;
- }
- }
-
- // link the document record to the window, and the other way around
-
- SetWRefCon( window, (long)hDocument );
- (*hDocument)->owner = window;
-
- // tho the toolbox should do this for us, let's do it ourselves to ensure the correct
- // value will be placed in the windowKind field
-
- SetWindowKind( window, kDocumentKind );
-
- // we got a window, so tell QuickDraw where to draw...
-
- SetPortWindowPort( window );
-
- // let's rename the window to keep a nice running count of the number of untitled
- // windows we have open(ed). we only need to do this for untitled windows (i.e.
- // pFileSpec == nil). we set the window title to the file name (if there is a file)
- // later on, and why waste time with doing this twice cause the first would be
- // moot and a waste
-
- if ( pFileSpec == nil )
- {
- gNewWindowCount++;
-
- GetIndString( titleString, rUntitledWindowStrings, strUntitledWindow );
- NumToString( gNewWindowCount, numString );
- HsoiConcatString( titleString, numString );
- SetWTitle( window, titleString );
- }
-
- /* let's adjust the window and it's various parts. We want the textRect (the initial
- viewRect and destRect of the WE instance) to be the same as the "viewRect" for
- printing...this way, what the user sees on the screen and what the user gets when
- they print will look and be the same.
-
- Depending on the size of the users monitor tho, this could extend off the end of
- the screen, so we'll have to adjust the size of the window to fit into the screen.
- At the same time, we don't want the window to cover the entire monitor...that's
- annoying for people with 21" monitors.
- */
-
- // first, figure out the ideal window size
-
- HsoiCalcIdealWindowSize( &windowRect );
-
- // assign this ideal size to the window
-
- SizeWindow( window, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, true );
-
- // figure out the text rect (and convert to a LongRect for passing to WENew)
-
- HsoiCalcTextRect( window, &textRect );
- WERectToLongRect( &textRect, &longTextRect );
-
- // it's possible that the window in it's ideal state will extend off the edges of
- // the monitor. readjust the window's portRect to fit inside the screen boundries
-
- HsoiAdjustWindowIntoScreen( window );
-
- // assign the preference's default settings to the port so things will show up
- // properly (need to do this before WENew() is called)
-
- GetFNum( gMyPrefs.defFont, &GetWindowPort(window)->txFont );
- GetWindowPort(window)->txFace = gMyPrefs.defFace;
- GetWindowPort(window)->txSize = gMyPrefs.defSize;
- if ( gHasColorQD )
- GetWindowPort(window)->rgbFgColor = gMyPrefs.defColor;
- else
- GetWindowPort(window)->fgColor = blackColor;
-
- /*
- create the WE instance, enabling certain functions. the functions we can/cannot
- do are listed in WASTE.h...and we're not supporting (right now) are:
- weDoReadOnly, weDoInhibitRecal.
-
- We'll use whatever our prefs setup calls for plus enabling undo support and the ability
- to use temporary memory.
- */
-
- err = WENew( &longTextRect, &longTextRect, gMyPrefs.defFeatures +
- weDoUndo +
- weDoUseTempMem, &we );
-
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindWENew, err, kErrNote );
- return err;
- }
-
- // save a reference to the window in the WE instance
- err = WESetInfo( weRefCon, &window, we );
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindWESetInfo, err, kErrNote );
- return err;
- }
-
- // now the other way around: save the WE handle in the document record
-
- (*hDocument)->we = we;
-
- // set the alignment to weFlushLeft so "slop recalc" is disabled
-
- WESetAlignment( weFlushLeft, we );
-
- // and set it again to whatever the preferences default is, but only if the
- // prefs default isn't weFlushLeft (why do it 2 times and waste processor time?)
-
- if( gMyPrefs.defAlign != weFlushLeft )
- WESetAlignment( gMyPrefs.defAlign, we );
-
- // and if they want Tab Hooks, let's install them
-
- // if ( pFileSpec == nil )
- // {
- if ( gMyPrefs.useTabHooks )
- {
- if ( WEIsTabHooks( we ) == false )
- {
- // left-align the text
- WESetAlignment( weFlushLeft, we );
-
- // install them
- WEInstallTabHooks( we );
- }
- else
- {
- // should never have to remove them, but we'll put this here for completeness
- WERemoveTabHooks( we );
- }
- }
- // }
-
- // create routine descriptors for the WASTE callbacks
-
- if ( sWEScroller == nil )
- {
- sWEScroller = NewWEScrollProc( hsoiTextScrolled );
- // sWEDragTranslator = NewWETranslateDragProc( HsoiTranslateDrag );
- }
-
- // set up our callbacks
-
- err = WESetInfo( weScrollProc, &sWEScroller, we );
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindWESetInfoScroll, err, kErrNote );
- return err;
- }
- /*
- err = WESetInfo( weTranslateDragHook, &sWEDragTranslator, we );
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindWESetInfoDrag, err, kErrNote );
- return err;
- }
- */
- // create a scroll bar from a 'CNTL' template
-
- // first vertical
-
- bar = GetNewControl( rScrollBarTemplate, window );
- if ( bar == nil )
- {
- err = -1;
- HsoiDoError( rWindowErrorStrings, strCrWindGetNewControlV, err, kErrNote );
- return err;
- }
-
- HiliteControl( bar, kControlDisabledPart );
-
- // attach a LongControl record to the scroll bar: this allows us to use long
- // settings and thus scroll text taller than 32,767 pixels
-
- err = HsoiLCAttach( bar );
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindLCAttachV, err, kErrNote );
- return err;
- }
-
- // save control handle in the document record
-
- ((*hDocument)->scrollBars).v = bar;
-
- // now horizontal
-
- bar = GetNewControl( rScrollBarTemplate, window );
- if ( bar == nil )
- {
- err = -1;
- HsoiDoError( rWindowErrorStrings, strCrWindGetNewControlH, err, kErrNote );
- return err;
- }
-
- HiliteControl( bar, kControlDisabledPart );
-
- // attach a LongControl record to the scroll bar: this allows us to use long
- // settings and thus scroll text taller than 32,767 pixels
-
- err = HsoiLCAttach( bar );
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindLCAttachH, err, kErrNote );
- return err;
- }
-
- // save control handle in the document record
-
- ((*hDocument)->scrollBars).h = bar;
-
- // ViewChanged() adjusts the scroll bars rectangles to the window frame
-
- HsoiViewChanged( window );
-
- // if pFileSpec is not nil, it points to a file to read, so let's read it!
-
- if ( pFileSpec != nil )
- {
- // turn the cursor into a wristwatch because this can be a lengthy operation
- // actually, we'll do this in HsoiReadTextFile cause that's really where the
- // time consumption is, and that way, any other function that calls HsoiReadTextFile
- // can get a VBL cursor, and we don't call one here to conflict with that
-
- //SetCursor( *gWaitCursor );
- //HsoiStartVBLSpinning();
-
- // retrieve file infomation
-
- err = FSpGetFInfo( pFileSpec, &fileInfo );
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindFSpGetFInfo, err, kErrNote );
- return err;
- }
-
- // make sure we recognize the file type
-
- // if it happened to be the prefs file they double clicked on, let's just skip it
- // and make sure to "nil" some things out
-
- if ( fileInfo.fdType == TYPE_PREFERENCES )
- {
- (*hDocument)->fileAlias = nil;
- SetCursor( &qd.arrow );
- goto skipit;
- }
-
- // we only deal with files of 'TEXT' and 'ttro' (text and read-only-text files)
-
- if ( (fileInfo.fdType != TYPE_TEXT ) && ( fileInfo.fdType != TYPE_TEXT_READ_ONLY ) )
- {
- err = -1;
- HsoiDoError( rWindowErrorStrings, strCrWindkTypeText, err, kErrNote );
- return err;
- }
-
- // read in the file
- err = HsoiReadTextFile( pFileSpec, we );
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindReadTextFile, err, kErrNote );
- return err;
- }
-
- // you _could_ call WECalText here and recalculate the line breaks,
- // but it's really unnecessary, and just tends to slow things down
-
- // set the window title to the file name
- SetWTitle( window, pFileSpec->name );
-
- // create an alias to keep track of the file
- err = NewAlias( nil, pFileSpec, &alHandle );
- (*hDocument)->fileAlias = (Handle)alHandle;
-
- // if the file is a read-only file (type 'ttro'), go ahead and enable
- // those flags
-
- if (fileInfo.fdType == TYPE_TEXT_READ_ONLY )
- WEFeatureFlag( weFReadOnly, weBitSet, we );
-
- // let's make sure the cursor is happy...
- //InitCursor();
- //HsoiStopVBLSpinning();
-
- } // end pFileSpec != nil
- else
- (*hDocument)->fileAlias = nil;
-
- skipit:
-
- // adjust scroll bar settings based on the total text height
-
- HsoiAdjustBars( window );
-
- // add to our open window count
-
- gNumWindows++;
-
- // finally! show the document window
-
- ShowWindow( window );
-
- // before we go, add this window to our Windows menu
-
- HsoiAddWindowToMenu( window );
-
- return err;
- }
-
- // the "low level" routine for axing a document window
-
- void HsoiDestroyWindow( WindowRef window )
- {
- DocumentHandle hDocument;
-
- // any function calling this routine ought to have done this already, but
- // just in case, we'll do it again to be sure.
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
-
- // get the window's document record
-
- hDocument = HsoiGetWindowDocument(window);
-
- // remove the window from the Windows menu
- // (we have to do this before we destroy much else since this function
- // relies upon the DocumentRecord attached to the window)
-
- HsoiRemoveWindowFromMenu( window );
-
- // destroy the WE record
-
- WEDispose( (*hDocument)->we );
-
- // destory the LongControl records attached to the scroll bars
-
- HsoiLCDetach( ((*hDocument)->scrollBars).v );
- HsoiLCDetach( ((*hDocument)->scrollBars).h );
-
- // dispose of the file alias, if any
-
- HsoiForgetHandle( &((*hDocument)->fileAlias) );
-
- // destroy the window record and all associated data structures
-
- DisposeWindow( window );
-
- // dispose of the document record
-
- DisposeHandle( (Handle)hDocument );
-
- // decrement our open window counter. note that we don't do this in HsoiDoClose
- // cause of how HsoiCreateWindow works...also, see HsoiDoRevert for more reasons.
-
- gNumWindows--;
-
- // adjust the menus to suit
-
- HsoiAdjustMenus();
-
- return;
- }
-
-
-
- /*
- This allows you to display the contents of the clipboard in a window. This is a special
- window however. For the most part, it will look like any regular text editing window
- (i.e. a window made by HsoiCreateWindow(), however, there will be an area just under
- the title bar to say what the clipboard contents is (text, sound, picture, etc). Also,
- you cannot edit this window (obviously) by typing on it, cutting and pasting, etc.
- nor do drag and drop editing on it.)
-
- so you know, the little area just under the title bar saying what the contents is
- (just like "Show Clipboard" in the Finder) isn't implimented yet...i'm still working
- on it.
-
- note the similarity of this code to the code used in HsoiCreateWindow(), and note
- the differences too. It'd be nice to just call HsoiCreateWindow() to do all this stuff,
- but due to a few differences in things, we have to just do it all over again.
- */
-
- OSErr HsoiShowClipboard( void )
- {
- OSErr err = noErr;
- DocumentHandle hDocument;
- Str255 windowTitle = NIL_STRING;
- Rect windowRect;
- Rect textRect;
- LongRect longTextRect;
- WEReference we;
- ControlRef bar;
- Boolean canPaste;
- Str255 tempStr = NIL_STRING;
-
- // to make life easier (i.e. kinda be lazy), if there is a currently playing
- // sound, we'll just stop it now. saves us the trouble from checking a million
- // times in the code below (when we try to allocate document record space,
- // allocate the window, allocate other things, bring up the "empty/unknown"
- // alert, etc....)
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
-
- if ( gClipboardWindow == nil )
- {
- // allocate a relocateable block to hold a document record
-
- hDocument = (DocumentHandle)NewHandleClear( sizeof( DocumentRecord ) );
- err = MemError();
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindNewPtrClear, err, kErrNote );
- return err;
- }
-
- // create the window from a 'WIND' template: the window is initially invisible
- // if ColorQuickDraw is available, create a color window
-
- if ( gHasColorQD )
- gClipboardWindow = GetNewCWindow( rMainWindow, nil, MOVE_TO_FRONT );
- else
- gClipboardWindow = GetNewWindow( rMainWindow, nil, MOVE_TO_FRONT );
-
- // make sure we got a window
-
- if ( gClipboardWindow == nil )
- {
- HsoiForgetHandle( (Handle *)&hDocument );
- HsoiDoError( rWindowErrorStrings, strCrWindGetNewWind, memFullErr, kErrNote );
- return memFullErr;
- }
-
- // link the document record to the window and the other way around
-
- SetWRefCon( gClipboardWindow, (long)hDocument );
- (*hDocument)->owner = gClipboardWindow;
-
- // this window is of type kClipboardWindow (the windowKind), so we must
- // set this field right for easy identification of the clipboard window (and so
- // the function HsoiIsClipboardWindow() will work right :)
-
- SetWindowKind( gClipboardWindow, kClipboardKind );
-
- // set the port to the clipboard window
-
- SetPortWindowPort( gClipboardWindow );
-
- // change the window title appropriately
-
- GetIndString( windowTitle, rClipboardWindowStrings, strClipboardWindowTitle );
- SetWTitle( gClipboardWindow, windowTitle );
-
- /* let's adjust the window and it's various parts. We want the textRect (the initial
- viewRect and destRect of the WE instance) to be the same as the "viewRect" for
- printing...this way, what the user sees on the screen and what the user gets when
- they print will look and be the same.
-
- Depending on the size of the users monitor tho, this could extend off the end of
- the screen, so we'll have to adjust the size of the window to fit into the screen.
- At the same time, we don't want the window to cover the entire monitor...that's
- annoying for people with 21" monitors.
- */
-
- // first, figure out the ideal window size
-
- HsoiCalcIdealWindowSize( &windowRect );
-
- // assign this ideal size to the window
-
- SizeWindow( gClipboardWindow, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, true );
-
- // it's possible that the window in it's ideal state will extend off the edges of
- // the monitor. readjust the window's portRect to fit inside the screen boundries
-
- HsoiAdjustWindowIntoScreen( gClipboardWindow );
-
- // figure out the text rect (and convert to a LongRect for passing to WENew)
-
- HsoiCalcTextRect( gClipboardWindow, &textRect );
- WERectToLongRect( &textRect, &longTextRect );
-
-
- // here's where we differ a bit. default preferences are NOT set here. we don't
- // really want the user to have too much control over how this window is created
- // cause of it's nature. however, if you want to have the default prefs settings
- // for this window, assign them here
-
- GetFNum( "\pGeneva", &GetWindowPort(gClipboardWindow)->txFont );
- GetWindowPort(gClipboardWindow)->txFace = normal;
- GetWindowPort(gClipboardWindow)->txSize = 9;
-
- // also note, in WENew, we don't just pass the prefs settings, but our own.
- // do NOT pass weDoDragAndDrop even if the computer can support drag and drop.
- // this is cause we want to prevent all dragging and dropping to this window
- // (tho this might change in the future...we might not allow drops to the window,
- // but we might allow people to select and drag from this window cause this might
- // make use of the clipboard a little easier (you can just paste what you want
- // into a document, like if you copied too much stuff to the clipboard).
- // Also note, we make this window read-only, and undo is not enabled (tho this
- // probably can/will change if drag and drop gets supported)
-
- err = WENew( &longTextRect, &longTextRect, weDoAutoScroll +
- weDoOutlineHilite +
- weDoIntCutAndPaste +
- weDoUseTempMem +
- weDoDrawOffscreen,
- &we);
-
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindWENew, err, kErrNote );
- return err;
- }
-
- err = WESetInfo( weRefCon, &gClipboardWindow, we );
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindWESetInfo, err, kErrNote );
- return err;
- }
-
- (*hDocument)->we = we;
-
- WESetAlignment( weFlushLeft, we );
-
- if ( sWEScroller == nil )
- {
- sWEScroller = NewWEScrollProc( hsoiTextScrolled );
- // sWEDragTranslator = NewWETranslateDragProc( HsoiTranslateDrag );
- }
-
- err = WESetInfo( weScrollProc, &sWEScroller, we );
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindWESetInfoScroll, err, kErrNote );
- return err;
- }
-
- // we shouldn't have this in here really, but we'll leave it in for now for
- // possible later support of dragging from this window
-
- /* err = WESetInfo( weTranslateDragHook, &sWEDragTranslator, we );
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindWESetInfoDrag, err, kErrNote );
- return err;
- }
- */
- // create a scroll bar from a 'CNTL' template
-
- // first vertical
-
- bar = GetNewControl( rScrollBarTemplate, gClipboardWindow );
- if ( bar == nil )
- {
- err = -1;
- HsoiDoError( rWindowErrorStrings, strCrWindGetNewControlV, err, kErrNote );
- return err;
- }
-
- HiliteControl( bar, kControlDisabledPart );
-
- // attach a LongControl record to the scroll bar: this allows us to use long
- // settings and thus scroll text taller than 32,767 pixels
-
- err = HsoiLCAttach( bar );
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindLCAttachV, err, kErrNote );
- return err;
- }
-
- // save control handle in the document record
-
- ((*hDocument)->scrollBars).v = bar;
-
- // now horizontal
-
- bar = GetNewControl( rScrollBarTemplate, gClipboardWindow );
- if ( bar == nil )
- {
- err = -1;
- HsoiDoError( rWindowErrorStrings, strCrWindGetNewControlH, err, kErrNote );
- return err;
- }
-
- HiliteControl( bar, kControlDisabledPart );
-
- // attach a LongControl record to the scroll bar: this allows us to use long
- // settings and thus scroll text taller than 32,767 pixels
-
- err = HsoiLCAttach( bar );
- if ( err != noErr )
- {
- HsoiDoError( rWindowErrorStrings, strCrWindLCAttachH, err, kErrNote );
- return err;
- }
-
- // save control handle in the document record
-
- ((*hDocument)->scrollBars).h = bar;
-
- // ViewChanged() adjusts the scroll bars rectangles to the window frame
-
- HsoiViewChanged( gClipboardWindow );
-
- // there's no file associated with this, so make sure to set the fileAlias to nil
-
- (*hDocument)->fileAlias = nil;
-
- // adjust scroll bar settings based on the total text height
-
- HsoiAdjustBars( gClipboardWindow );
- }
- else
- {
- // we have the window. let's hide it while we update things
- HideWindow( gClipboardWindow );
-
- // and we need to get the WASTE instance for that window
-
- we = HsoiGetWindowWE( gClipboardWindow );
- }
-
- // ok, now we have a window (either cause it was already around, or cause we just
- // created one). Now, it's time to put something into it.
-
- // first, we check to see if there's anything on the clipboard to paste.
- // (disable the read-only bit if it's set cause WECanPaste checks for this)
-
- WEFeatureFlag( weFReadOnly, weBitClear, we );
-
- canPaste = WECanPaste( we );
-
- // now, let's paste in whatever we got
-
- if ( canPaste )
- {
- // since WEPaste (and it's call to WEInsert) and WEDElete could take a long time
- // (like if the scrap is HUGE) can take a long time to perform, let's spin
- // our cursor. Again (as with just about all my uses of the VBL spinning cursor)
- // this is good to do cause otherwise it sorta looks like the computer froze up
- // and this could worry the user...if the cursor is spinning, at least the
- // user is getting some feedback that the computer is running...and heck, if
- // we know the cursor is supposed to spin and it freezes up, then we probably
- // do have some sort of bad error and the computer is probably fubar
-
- // start the VBL spinning cursor
-
- HsoiStartVBLSpinning();
-
- // let's take whatever's currently in the window and delete it
-
- WESetSelection( 0, MAXLONG, we );
- err = WEDelete( we );
-
- // now paste whatever's on the scrap into the window
-
- //err = WEPaste( we );
- err = WEObjectsPaste( we );
-
- // and stop the spinning cursor
-
- HsoiStopVBLSpinning();
- }
- else
- {
- // we shouldn't use the prefs error alert box for this, but it's the "cleanest"
- // error box for use to use for this. this will change when i get the error handlers
- // all re-written
-
- GetIndString( tempStr, rClipboardWindowStrings, strClipboardEmpty );
- ParamText( tempStr, NIL_STRING, NIL_STRING, NIL_STRING );
- NoteAlert( rPrefsErrorAlert, HsoiGetMyStandardDialogFilter() );
-
- // and through testing some other stuff, i found this as a bug...if !canPaste,
- // and there was something already in the window (like from a previous "sucessful"
- // ShowClipboard), the "old" window contents showed up! not good...we should
- // make sure the window contains nothing. so, simple fix:
-
- // and going with the above ( if canPaste ) thing about a long time, since
- // WEDelete could take a bit to do, let's sping the cursor
-
- HsoiStartVBLSpinning();
-
- WESetSelection( 0, MAXLONG, we );
- err = WEDelete( we );
-
- HsoiStopVBLSpinning();
- }
-
- // set the read-only bit. users should not be able to cut/copy/paste to/from this
- // window for obvious reasons
-
- WEFeatureFlag( weFReadOnly, weBitSet, we );
-
- // now we can show it and all that good stuff
-
- ShowWindow( gClipboardWindow );
- SelectWindow( gClipboardWindow );
- SetPortWindowPort( gClipboardWindow );
-
- // force a menu update
-
- HsoiAdjustMenus();
-
-
- return err;
- }
-
-
- // since this is called from within HsoiDoUpdate(), there is no need to call BeginUpdate()
- // EndUpdate() and to set the port to the window, cause HsoiDoUpdate() handles all of
- // that stuff. however, if you call HsoiDoClipboardUpdate() from anywhere else, see if
- // you can just call HsoiDoUpdate() instead, else make sure to set things properly
- // and call proper functions so a proper update can be done.
-
- void HsoiDoClipboardUpdate( WindowRef window )
- {
- if ( window != gClipboardWindow )
- {
- SysBeep(1);
- return;
- }
-
- if ( !HsoiIsClipboardWindow( window ) )
- {
- SysBeep(1);
- SysBeep(1);
- return;
- }
-
- /* this isn't working right...i'm trying to have it draw a string that will tell
- the user what type of data the clipboard contains. it's not working.
- (it would be just like the "Show Clipboard" in the Finder. And if this
- got working, that alert in HsoiShowClipboard() about empty/unknown would
- then no longer be necessary. if it was empty, say so, if it's known,
- say what it is, and default to unknown.
-
- oh well, i've pretty much removed all the relevant code in HsoiShowClipboard
- that would deal with this, and this function can just wait.
-
- First, my drawing routines might not be right. second, I'm reusing a lot
- of CreateWindow util routines to do this, and that's probably the problem. I
- probably need to create a whole new slew of things just to work with this
- (a new Resize, a new ViewChanged, a new scroll proc, etc etc)
-
- second, and i think due to the above, when you select stuff in the window
- (and probably also due to the following code, it draws funky lines all over
- the place...it doesn't look good.
- /*
- PenNormal();
-
- MoveTo( window->portRect.left + 1, window->portRect.top - 1 );
- ShowPen();
- LineTo( window->portRect.right - 1, window->portRect.top - 1 );
- */
-
-
-
- return;
- }